package com.black.utils.config.loader;

import com.alibaba.fastjson2.JSONObject;
import com.black.utils.file.FileTypeEnum;
import com.black.utils.file.FileUtil;
import com.black.utils.file.compress.jar.entity.JarInfo;
import com.black.utils.file.compress.jar.entity.TextInfo;
import com.black.utils.file.compress.jar.visitor.JarClassVisitor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


/**
 * Jar文件加载器
 *
 * @author ylx
 */
@Slf4j
@Getter
public class JarLoader extends Loader {

    private final Map<String, JarInfo> jarInfoMap = new HashMap<>();
    private static final String JAR_SUFFIX = FileTypeEnum.JAR.getSuffix();

    @Override
    public JSONObject loadJsonConfig(String sourcePath) throws IOException {
        // 加载JAR文件
        File dir = new File(sourcePath);
        File[] files = dir.listFiles(file -> file.isFile() && file.getName().endsWith(JAR_SUFFIX));
        // 遍历JAR文件
        long start = System.currentTimeMillis();
        for (File file : files) {
            String jarFilePath = file.getAbsolutePath();
            String server = file.getName().replaceAll(JAR_SUFFIX, "");
            jarInfoMap.put(server, new JarInfo(server));
            log.info("加载{},{}", server, jarFilePath);
            long startServer = System.currentTimeMillis();
            try (JarFile jarFile = new JarFile(jarFilePath)) {
                loadJar(jarFile, server);
            } catch (IOException e) {
                log.error(e.getMessage());
                throw e;
            }
            log.info("加载{},{}{}{}", server, "耗时", (System.currentTimeMillis() - startServer), "ms");
        }
        log.info("加载完成, 耗时{}{}", (System.currentTimeMillis() - start), "ms");
        return new JSONObject(jarInfoMap);
    }

    @Override
    public List<FileTypeEnum> getTypes() {
        return Collections.singletonList(FileTypeEnum.JAR);
    }

    private void loadJar(JarFile jarFile, String server) throws IOException {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            loadEntry(jarFile, entry, server);
        }
    }

    private void loadEntry(JarFile jarFile, JarEntry jarEntry, String server) throws IOException {
        String name = jarEntry.getName();
        try (InputStream inputStream = jarFile.getInputStream(jarEntry)) {
            byte[] bytes = FileUtil.loadFile(inputStream);
            if (bytes == null) {
                return;
            }
            JarInfo jarInfo = jarInfoMap.get(server);
            if (FileTypeEnum.JAR.is(name)) {
                // 递归处理嵌套的 JAR 文件
                // 创建 JarFile 对象以处理嵌套 JAR
                File tempFile = File.createTempFile("temp-jar", JAR_SUFFIX);
                FileUtil.saveBytes(tempFile, bytes);
                loadJar(new JarFile(tempFile), server);
                // 删除临时文件
                tempFile.delete();
            } else if (FileTypeEnum.CLASS.is(name)) {
                // 处理 .class 文件
                // 使用 ClassReader 解析字节码
                ClassReader cr = new ClassReader(bytes);
                JarClassVisitor visitor = new JarClassVisitor();
                cr.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                jarInfo.addClass(visitor.getClassInfo());
            } else if (FileTypeEnum.isText(name)) {
                // 处理 .json 文件
                // 转换为字符串
                TextInfo textInfo = new TextInfo(name, FileTypeEnum.getExtension(name));
                textInfo.setContent(bytes);
                jarInfo.addText(textInfo);
            } else if (FileTypeEnum.PROPERTIES.is(name)) {
                Properties properties = new Properties();
                properties.load(inputStream);
                jarInfo.addProperties(properties);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw e;
        }
    }
}
